/* PatientenErzeugung.java
 *
 * Author:	Malcolm Staudacher
 * Date:	May 2, 2008
 *
 */
package ocumed.applikation.domainhandling;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import ocumed.applikation.exceptions.BenutzerNotFoundException;
import ocumed.applikation.exceptions.NoArztException;
import ocumed.domaene.behandlung.DBehandlung;
import ocumed.domaene.behandlung.DLeistungscode;
import ocumed.domaene.behandlung.DMedikament;
import ocumed.domaene.behandlung.DMedikamentVerordnung;
import ocumed.domaene.behandlung.DPosition;
import ocumed.domaene.benutzerverwaltung.arztverwaltung.DArzt;
import ocumed.domaene.dokument.DRezept;
import ocumed.domaene.patientenverwaltung.DOrt;
import ocumed.domaene.patientenverwaltung.DPatient;
import ocumed.persistenz.dao.BehandlungDAO;
import ocumed.persistenz.dao.DAOFactory;
import ocumed.persistenz.dao.LeistungDAO;
import ocumed.persistenz.dao.PatientDAO;
import ocumed.persistenz.dao.PositionDAO;
import ocumed.persistenz.dao.RezeptDAO;
import ocumed.persistenz.hibernate.OcBehandlung;
import ocumed.persistenz.hibernate.OcLeistung;
import ocumed.persistenz.hibernate.OcMediver;
import ocumed.persistenz.hibernate.OcPatient;
import ocumed.persistenz.hibernate.OcPosition;
import ocumed.persistenz.hibernate.OcRezept;
import ocumed.teams.IBehandlung;
import ocumed.teams.ILeistung;
import ocumed.teams.IMediver;
import ocumed.teams.IPatient;
import ocumed.teams.IPosition;
import ocumed.teams.IRezept;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * cache and instantiation for domain patient's. these objects don't have any
 * reference to objects from hibernate, therefore all references have to be
 * instantiated beforehand.
 * 
 * @author Malcolm Staudacher
 * 
 */
public class PatientenErzeugung {

	private static final Log log = LogFactory.getLog(PatientenErzeugung.class);
	// access the domain handler
	private DomainHandler dh;
	private PatientDAO m_patientDAO;
	private BehandlungDAO m_behandlungDAO;
	private RezeptDAO m_rezeptDAO;
	private LeistungDAO m_leistungDAO;

	public PatientenErzeugung() {
		dh = DomainHandler.getInstance();
		m_patientDAO = DAOFactory.get(DAOFactory.RDBMS).getPatientDAO();
		m_behandlungDAO = DAOFactory.get(DAOFactory.RDBMS).getBehandlungDAO();
		m_rezeptDAO = DAOFactory.get(DAOFactory.RDBMS).getRezeptDAO();
		m_leistungDAO = DAOFactory.get(DAOFactory.RDBMS).getLeistungDAO();
	}

	/**
	 * @return
	 * @throws BenutzerNotFoundException
	 * @throws NoArztException
	 */
	public List<IPatient> getDPatienten() throws NoArztException,
			BenutzerNotFoundException {
		m_patientDAO.start();
		List<IPatient> allePatienten = new Vector<IPatient>();
		for (OcPatient p : m_patientDAO.findAll()) {
			allePatienten.add(getDPatient(p));
		}
		return allePatienten;
	}

	/**
	 * Gets all patients for the specified doctor. DO NOT REMOVE !!!
	 * 
	 * @param arzt
	 *            The doctor, whose patients to get.
	 * 
	 * @return Interfaces to all patients of the specified doctor.
	 */
	public Set<IPatient> getDPatienten(DArzt arzt) {
		m_patientDAO.start();
		Set<IPatient> arztPatienten = new HashSet<IPatient>();
		for (OcPatient p : m_patientDAO.findAll()) {
			if (p.getOcBenutzerByArztid().getBenutzerid() == arzt
					.getBenutzerid()) {
				arztPatienten.add(getDPatient(p, arzt));
			}
		}
		return arztPatienten;
	}

	/**
	 * Primary method for getting a domain patient from db (hibernate).
	 * 
	 * @param ocPatient
	 * @return
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DPatient getDPatient(OcPatient ocPatient) throws NoArztException,
			BenutzerNotFoundException {
		DPatient pat = null;

		// cache lookup
		pat = getP(ocPatient.getPatientid());
		if (pat != null) {
			return pat;
		}

		// load references beforehand
		DArzt arzt = DomainHandler.be.getDArzt(ocPatient
				.getOcBenutzerByArztid());
		DOrt ort = DomainHandler.oe.getDOrt(ocPatient.getOcOrt());

		// create the domain object
		pat = new DPatient(ocPatient.getPatientid(), arzt, ort, ocPatient
				.getPatientvorname(), ocPatient.getPatientnachname(), ocPatient
				.getPatientgeschlecht(), ocPatient.getPatientanrede(),
				ocPatient.getPatientsvn(), ocPatient.getPatientgebdatum(),
				ocPatient.getPatienttel(), ocPatient.getPatientfax(), ocPatient
						.getPatientemail(), ocPatient.getPatientbesitztecard(),
				ocPatient.getPatientadresse());

		// get treatments
		pat.setiBehandlungs(getiBehandlungen(ocPatient, pat));

		// set prescriptions
		pat.setiMedivers(getVerordnungen(ocPatient, pat));

		// get appointments
		pat.setiTermins(DomainHandler.te.getDTermine(ocPatient));

		// get prescriptions
		pat.setiRezepts(getiRezepte(ocPatient, pat));

		// add patient to cache
		dh.getPatienten().add(pat);

		return pat;
	}

	/**
	 * Returns a new patient when the DArzt domain object already exists. This
	 * method is needed for getting all patients for one doctor. The DArzt
	 * domain object is created first, to avoid mutual dependency of the object
	 * creators. DO NOT REMOVE THIS METHOD!!!
	 * 
	 * @param ocPatient
	 * @param arzt
	 * @return
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DPatient getDPatient(OcPatient ocPatient, DArzt arzt)
			throws NoArztException, BenutzerNotFoundException {
		DPatient pat = getP(ocPatient.getPatientid());

		if (pat != null) {
			return pat;
		}

		DOrt ort = DomainHandler.oe.getDOrt(ocPatient.getOcOrt());
		pat = new DPatient(ocPatient.getPatientid(), arzt, ort, ocPatient
				.getPatientvorname(), ocPatient.getPatientnachname(), ocPatient
				.getPatientgeschlecht(), ocPatient.getPatientanrede(),
				ocPatient.getPatientsvn(), ocPatient.getPatientgebdatum(),
				ocPatient.getPatienttel(), ocPatient.getPatientfax(), ocPatient
						.getPatientemail(), ocPatient.getPatientbesitztecard(),
				ocPatient.getPatientadresse());
		// dh.m_patienten.add(pat);

		return pat;
	}

	/**
	 * create from id only. first, get the hibernate object
	 * 
	 * @param patientId
	 * @return
	 * @throws NoArztException
	 * @throws BenutzerNotFoundException
	 */
	public DPatient getDPatient(int patientId) throws NoArztException,
			BenutzerNotFoundException {
		m_patientDAO.start();
		OcPatient ocPat = m_patientDAO.findById(patientId);

		return getDPatient(ocPat);
	}

	/**
	 * Gets all DBehandlung domain objects for the specified patient.
	 * 
	 * @param p
	 * @return
	 */
	public Set<DBehandlung> getBehandlungen(OcPatient p, DPatient dp) {
		Set<OcBehandlung> ocbs = p.getOcBehandlungs();
		Set<DBehandlung> dbs = new HashSet<DBehandlung>();

		for (OcBehandlung ocb : ocbs) {
			dbs.add(getDBehandlung(ocb, dp));
		}

		return dbs;
	}

	public Set<IBehandlung> getiBehandlungen(OcPatient p, DPatient dp) {
		Set<IBehandlung> ibs = new HashSet<IBehandlung>();

		for (DBehandlung db : getBehandlungen(p, dp)) {
			ibs.add(db);
		}

		return ibs;
	}

	/**
	 * Creates a DBehandlung domain object from the data object. The patient
	 * must be set after creation.
	 * 
	 * @param ocb
	 *            The data object.
	 * @return A DBehandlung domain object.
	 */
	private DBehandlung getDBehandlung(OcBehandlung ocb, DPatient dp) {
		DBehandlung db = getB(ocb.getBehandlungid());

		// cache lookup
		if (db != null) {
			return db;
		}

		db = new DBehandlung(ocb.getBehandlungid(), dh.be.getDArzt(ocb
				.getOcBenutzer()), ocb.getBehandlungvon(), ocb
				.getBehandlungbis(), ocb.getBehandlungdiagnose(), ocb
				.getBehandlungmitueberweisung(), ocb
				.getBehandlungmitverschein(), ocb
				.getBehandlungmitterminvermerk(), ocb.getBehandlungstatus());

		// set patient
		db.setiPatient(dp);

		// set positions
		db.setiPositions(getIPositionen(ocb, db));

		dh.getBehandlungen().add(db);

		return db;
	}

	public Set<IRezept> getiRezepte(OcPatient ocp, DPatient dp) {
		Set<IRezept> irs = new HashSet<IRezept>();

		for (DRezept dr : getRezepte(ocp, dp)) {
			irs.add(dr);
		}

		return irs;
	}

	public Vector<DRezept> getRezepte(OcPatient p, DPatient dp) {
		Vector<DRezept> drs = new Vector<DRezept>();
		Set<OcRezept> ocrs = p.getOcRezepts();

		for (OcRezept ocr : ocrs) {

			if (ocr.getOcPatient().getPatientid() == p.getPatientid()) {
				DRezept dr = getDRezept(ocr, dp);
				drs.add(dr);
			}
		}

		return drs;
	}

	/**
	 * Primary method for materializing a DRezept domain object. The references
	 * to patient and to doctor must be set after creation!
	 * 
	 */
	private DRezept getDRezept(OcRezept ocr, DPatient dp) {
		DRezept dr = getR(ocr.getRezeptid());

		// cache lookup
		if (dr != null) {
			return dr;
		}

		dr = new DRezept(ocr.getRezeptid(), ocr.getRezeptdatum().getTime(), ocr
				.getRezepttext(), ocr.getRezeptletzterdruck().getTime(), ocr
				.getRezeptbezeichnung(), getDBehandlung(ocr.getOcBehandlung(),
				dp));

		// set reference to patient
		dr.setiPatient(dp);

		return dr;
	}

	private Set<IMediver> getVerordnungen(OcPatient ocp, DPatient pat) {
		Set<IMediver> ims = new HashSet<IMediver>();
		Set<OcMediver> ocms = ocp.getOcMedivers();

		for (OcMediver ocm : ocms) {
			ims.add(getDMedVer(ocm, pat));
		}

		return ims;
	}

	private DMedikamentVerordnung getDMedVer(OcMediver ocmv, DPatient pat) {
		DMedikamentVerordnung dmv = getM(ocmv.getMediverid());

		// cache lookup
		if (dmv != null) {
			return dmv;
		}

		DMedikament dm = dh.me.getDMedikament(ocmv.getOcMedikament());

		dmv = new DMedikamentVerordnung(ocmv.getMediverbis().getTime(), ocmv
				.getMedivermenge(), ocmv.getMedivervon().getTime(), ocmv
				.getMediverdosierung(), ocmv.getMediverid(), pat, dm);

		dh.getVerordnungen().add(dmv);
		return dmv;
	}

	private Set<IPosition> getIPositionen(OcBehandlung ocb, DBehandlung db) {
		Set<OcPosition> ocps = ocb.getOcPositions();
		Set<IPosition> ips = new HashSet<IPosition>();

		for (OcPosition ocp : ocps) {
			ips.add(getDPosition(ocp, db));
		}

		return ips;
	}

	private DPosition getDPosition(OcPosition ocp, DBehandlung db) {
		DPosition dp = getPos(ocp.getPositionid());

		if (dp != null) {
			return dp;
		}

		DLeistungscode dl = getDLeistung(ocp.getOcLeistung());
		Boolean schatten = ocp.getPositionbestimmtschatten() == PositionDAO.bestimmtSchatten;
		Boolean fraktometer = ocp.getPositionbestimmtfraktrometer() == PositionDAO.bestimmtFraktrometer;
		Boolean phoropter = ocp.getPositionbestimmtphoropter() == PositionDAO.bestimmtPhoropter;

		return new DPosition(ocp.getPositionid(), ocp.getPositiontext(), db, dl, 
				ocp.getPositionsehtestl(), ocp.getPositionsehtestr(),
				ocp.getPositionfehll(), ocp.getPositionfehlr(),
				schatten, fraktometer, phoropter, ocp.getPositiontyp());
	}

	
	/**
	 * Gets all DLeistungscode objects from the corresponding data objects.
	 * 
	 * @return All DLeistungscode objects from the corresponding data objects.
	 */
	public Vector<DLeistungscode> getLeistungscodes() {
		Vector<DLeistungscode> dlcs = new Vector<DLeistungscode>();
		m_leistungDAO.start();
		List<OcLeistung> codes = m_leistungDAO.findAll();

		for (OcLeistung ol : codes) {
			dlcs.add(getDLeistung(ol));
		}

		dh.setLeistungscodes(dlcs);
		return dlcs;
	}

	private DLeistungscode getDLeistung(OcLeistung ocl) {
		DLeistungscode dlc = getL(ocl.getLeistungid());

		// cache lookup
		if (dlc != null) {
			return dlc;
		}

		dlc = new DLeistungscode(ocl.getLeistungid(), ocl
				.getLeistungbezeichnung(), ocl.getLeistungcode());

		dh.getLeistungscodes().add(dlc);
		return dlc;
	}

	public ILeistung getILeistung(int id) {
		m_leistungDAO.start();
		OcLeistung ol = m_leistungDAO.findById(id);
		return getDLeistung(ol);
	}

	// Methods for cache lookup

	/**
	 * cache lookup
	 * 
	 * @param id
	 * @return
	 */
	private DPatient getP(int id) {

		for (DPatient pat : dh.getPatienten()) {

			if (pat.getPatientid() == id) {
				return pat;
			}

		}

		return null;
	}

	private DBehandlung getB(int id) {

		for (DBehandlung db : dh.getBehandlungen()) {

			if (db.getBehandlungid() == id) {
				return db;
			}

		}

		return null;
	}

	private DRezept getR(int id) {

		for (DRezept dr : dh.getRezepte()) {

			if (dr.getRezeptid() == id) {
				return dr;
			}

		}

		return null;
	}

	private DMedikamentVerordnung getM(int id) {

		for (DMedikamentVerordnung mv : dh.getVerordnungen()) {

			if (mv.getMediverid() == id) {
				return mv;
			}

		}

		return null;
	}

	private DLeistungscode getL(int id) {

		for (DLeistungscode lc : dh.getLeistungscodes()) {

			if (lc.getLeistungid() == id) {
				return lc;
			}

		}

		return null;
	}

	private DPosition getPos(int id) {

		for (DPosition pos : dh.getPositionen()) {

			if (pos.getPositionid() == id) {
				return pos;
			}

		}

		return null;
	}

}
